21  차원 축소

Keywords

python, 전처리, 통계, 가설검정, 기계학습, 회귀, 분류, 군집, 모델 학습, 모델 평가

차원 축소(Dimensionality Reduction)는 여러 개의 입력 변수(차원)를 더 적은 수의 변수로 변환하는 과정이다. 원본 특성을 그대로 사용하지 않고 새로운 특성(축)을 만들어 데이터를 표현하는 방법으로, 특성 추출(Feature Extraction)을 포함하는 개념이다. 차원 축소는 고차원 데이터의 시각화, 학습 속도 향상, 일반화 성능 개선에 핵심적인 역할을 한다. 이 장에서는 PCA, t-SNE, UMAP 등 주요 차원 축소 기법의 원리와 실무 활용법을 학습한다.

예제: 데이터 로드

import seaborn as sns
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler

# 데이터 로드
df = sns.load_dataset("penguins").dropna()
X = df[["bill_length_mm", "bill_depth_mm", "flipper_length_mm", "body_mass_g"]]
y = df["species"]

print("데이터 크기:", X.shape)
print("원본 차원:", X.shape[1])
데이터 크기: (333, 4)
원본 차원: 4

21.1 차원 축소의 개념과 필요성

차원 축소는 고차원 데이터를 저차원으로 변환하여 데이터를 효율적으로 표현하고 분석한다.

차원 축소가 필요한 이유

문제 설명 차원 축소로 해결
차원의 저주 변수가 많아질수록 필요한 샘플 수 기하급수적 증가 핵심 변수만 유지
시각화 어려움 3차원 이상은 직접 시각화 불가 2D/3D로 변환
학습 시간 증가 변수가 많을수록 계산량 증가 연산량 감소
다중공선성 상관성 높은 변수들이 모델 불안정화 독립적인 축 생성
노이즈 불필요한 변수가 노이즈 포함 주요 신호만 추출
메모리 사용 고차원 데이터는 저장 공간 많이 차지 데이터 압축

차원 축소의 기대 효과

효과 설명
데이터 구조 단순화 복잡한 데이터를 간결하게 표현
학습 속도 향상 변수 수 감소로 연산량 감소
일반화 성능 개선 과적합 위험 감소
시각화 가능 2D/3D로 변환하여 패턴 파악
저장 공간 절약 데이터 압축 효과

21.1.1 특성 선택 vs 차원 축소 (특성 추출)

특성 선택 vs 차원 축소 비교

구분 특성 선택 (Feature Selection) 차원 축소 (Feature Extraction)
정의 기존 변수 중 일부를 선택 기존 변수를 변환하여 새로운 변수 생성
원본 변수 그대로 유지 변환/조합
변수 의미 유지됨 해석 어려워짐
정보 손실 선택 안 된 변수의 정보 손실 압축 과정에서 일부 손실
예시 상관계수로 3개 변수 선택 PCA로 2개 주성분 생성
해석력 높음 낮음
적용 도메인 지식 중요 데이터 구조 의존

예제: 특성 선택 vs 차원 축소

from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.decomposition import PCA

# 데이터 표준화
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 특성 선택: 원본 변수 중 2개 선택
selector = SelectKBest(score_func=f_classif, k=2)
X_selected = selector.fit_transform(X, y)
selected_features = X.columns[selector.get_support()]

print("=== 특성 선택 ===")
print(f"선택된 변수: {selected_features.tolist()}")
print(f"변수 의미: 원본 그대로 유지")

# 차원 축소: 새로운 2개 주성분 생성
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

print("\n=== 차원 축소 (PCA) ===")
print(f"생성된 변수: PC1, PC2")
print(f"변수 의미: 원본 변수들의 선형 조합")
print(f"설명 분산: {pca.explained_variance_ratio_}")
=== 특성 선택 ===
선택된 변수: ['bill_length_mm', 'flipper_length_mm']
변수 의미: 원본 그대로 유지

=== 차원 축소 (PCA) ===
생성된 변수: PC1, PC2
변수 의미: 원본 변수들의 선형 조합
설명 분산: [0.68633893 0.19452929]

21.2 선형 차원 축소: PCA

PCA(Principal Component Analysis, 주성분 분석)는 가장 널리 사용되는 선형 차원 축소 방법이다.

PCA의 핵심 아이디어

  • 데이터의 분산을 가장 잘 설명하는 방향을 찾음
  • 기존 변수의 선형 결합으로 새로운 축 생성
  • 새로운 축을 주성분(Principal Component)이라 부름
  • 각 주성분은 서로 직교(독립)

PCA의 작동 원리

  1. 데이터 표준화 (평균 0, 분산 1)
  2. 공분산 행렬 계산
  3. 고유값과 고유벡터 계산
  4. 고유값이 큰 순서로 주성분 선택
  5. 원본 데이터를 새로운 주성분 공간으로 투영

PCA의 특징

특징 설명
선형 변환 원본 변수의 선형 조합
분산 최대화 데이터가 가장 퍼진 방향 찾기
직교성 주성분들은 서로 독립
스케일 민감 표준화 필수
비지도 학습 타겟 정보 사용 안 함
해석 가능 각 주성분의 기여도 확인 가능

21.2.1 PCA 실습

예제: PCA 적용

from sklearn.decomposition import PCA

# 데이터 표준화
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# PCA 적용 (2차원으로 축소)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

print("=== PCA 결과 ===")
print(f"원본 차원: {X.shape[1]}")
print(f"축소 차원: {X_pca.shape[1]}")
print(f"\n각 주성분의 설명 분산 비율:")
for i, var in enumerate(pca.explained_variance_ratio_, 1):
    print(f"  PC{i}: {var:.4f} ({var*100:.2f}%)")
print(f"\n누적 설명 분산: {pca.explained_variance_ratio_.sum():.4f} ({pca.explained_variance_ratio_.sum()*100:.2f}%)")
=== PCA 결과 ===
원본 차원: 4
축소 차원: 2

각 주성분의 설명 분산 비율:
  PC1: 0.6863 (68.63%)
  PC2: 0.1945 (19.45%)

누적 설명 분산: 0.8809 (88.09%)

예제: 주성분 구성 확인

# 각 주성분을 구성하는 원본 변수의 기여도
components_df = pd.DataFrame(
    pca.components_,
    columns=X.columns,
    index=[f'PC{i+1}' for i in range(pca.n_components_)]
)

print("\n=== 주성분 구성 (변수별 기여도) ===")
print(components_df.round(3))

# 시각화
plt.figure(figsize=(10, 5))
sns.heatmap(components_df, annot=True, cmap='coolwarm', center=0, 
            cbar_kws={'label': 'Contribution'})
plt.title("PCA Component Loadings")
plt.tight_layout()
plt.show()

=== 주성분 구성 (변수별 기여도) ===
     bill_length_mm  bill_depth_mm  flipper_length_mm  body_mass_g
PC1           0.454         -0.399              0.577        0.550
PC2           0.600          0.796              0.006        0.076

예제: 설명 분산 시각화

# 모든 주성분의 설명 분산
pca_full = PCA()
pca_full.fit(X_scaled)

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 개별 설명 분산
axes[0].bar(range(1, len(pca_full.explained_variance_ratio_)+1), 
            pca_full.explained_variance_ratio_)
axes[0].set_xlabel('Principal Component')
axes[0].set_ylabel('Explained Variance Ratio')
axes[0].set_title('Variance Explained by Each Component')
axes[0].set_xticks(range(1, len(pca_full.explained_variance_ratio_)+1))

# 누적 설명 분산
cumsum = np.cumsum(pca_full.explained_variance_ratio_)
axes[1].plot(range(1, len(cumsum)+1), cumsum, 'o-', linewidth=2)
axes[1].axhline(y=0.95, color='r', linestyle='--', label='95% threshold')
axes[1].set_xlabel('Number of Components')
axes[1].set_ylabel('Cumulative Explained Variance')
axes[1].set_title('Cumulative Variance Explained')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
axes[1].set_xticks(range(1, len(cumsum)+1))

plt.tight_layout()
plt.show()

print(f"\n95% 분산 설명에 필요한 주성분 수: {np.argmax(cumsum >= 0.95) + 1}")


95% 분산 설명에 필요한 주성분 수: 3

예제: PCA 시각화

# 2D 시각화
plt.figure(figsize=(10, 6))
scatter = plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y.astype('category').cat.codes, 
                     cmap='viridis', alpha=0.6, edgecolors='k')
plt.xlabel(f'PC1 ({pca.explained_variance_ratio_[0]*100:.1f}%)')
plt.ylabel(f'PC2 ({pca.explained_variance_ratio_[1]*100:.1f}%)')
plt.title('PCA: 2D Projection')
plt.colorbar(scatter, label='Species')
plt.legend(handles=scatter.legend_elements()[0], labels=y.unique())
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

21.2.2 PCA 적정 차원 수 선택

차원 수 선택 기준

방법 설명 기준
누적 설명 분산 전체 분산의 일정 비율 유지 일반적으로 85~95%
Elbow Method 설명 분산 증가율이 급감하는 지점 그래프에서 꺾이는 점
Kaiser Rule 고유값 > 1인 주성분 선택 표준화 데이터 기준
도메인 지식 해석 가능한 차원 수 실무 목적에 맞게

예제: 적정 차원 수 자동 선택

# 95% 분산 설명하는 차원 수 자동 선택
pca_auto = PCA(n_components=0.95)
X_pca_auto = pca_auto.fit_transform(X_scaled)

print(f"=== 자동 차원 선택 (95% 분산) ===")
print(f"선택된 주성분 수: {pca_auto.n_components_}")
print(f"실제 설명 분산: {pca_auto.explained_variance_ratio_.sum():.4f}")
=== 자동 차원 선택 (95% 분산) ===
선택된 주성분 수: 3
실제 설명 분산: 0.9730

21.3 비선형 차원 축소

선형 변환으로는 복잡한 데이터 구조를 표현하기 어려운 경우 비선형 차원 축소를 사용한다.

선형 vs 비선형 차원 축소

구분 선형 (PCA) 비선형 (t-SNE, UMAP)
가정 데이터가 선형 부분공간에 존재 데이터가 비선형 다양체에 존재
변환 선형 조합 비선형 변환
전역 구조 잘 보존 보존 정도 다름
국소 구조 보존 못할 수 있음 잘 보존
주 용도 전처리, 압축 시각화
역변환 가능 불가능

21.3.1 t-SNE

t-SNE(t-Distributed Stochastic Neighbor Embedding)는 데이터 간 이웃 관계를 확률적으로 모델링하는 비선형 차원 축소 방법이다.

t-SNE의 특징

특징 설명
비선형 복잡한 구조 표현 가능
국소 구조 보존 가까운 점들을 가깝게 유지
시각화 특화 주로 2D/3D 시각화 목적
계산 비용 높음 대용량 데이터에 느림
비결정적 초기화에 따라 결과 다름
역변환 불가 모델 입력용 부적합

t-SNE 하이퍼파라미터

파라미터 설명 권장값
n_components 출력 차원 2 또는 3
perplexity 이웃 수 (5~50) 30 (기본값)
learning_rate 학습률 200 (기본값)
n_iter 최적화 반복 횟수 1000+
random_state 재현성 고정값

예제: t-SNE 적용

from sklearn.manifold import TSNE

# t-SNE 적용
tsne = TSNE(n_components=2, random_state=42, perplexity=30)
X_tsne = tsne.fit_transform(X_scaled)

print("=== t-SNE 결과 ===")
print(f"원본 차원: {X.shape[1]}")
print(f"축소 차원: {X_tsne.shape[1]}")

# 시각화
plt.figure(figsize=(10, 6))
scatter = plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y.astype('category').cat.codes,
                     cmap='viridis', alpha=0.6, edgecolors='k')
plt.xlabel('t-SNE Component 1')
plt.ylabel('t-SNE Component 2')
plt.title('t-SNE: 2D Projection')
plt.legend(handles=scatter.legend_elements()[0], labels=y.unique())
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
=== t-SNE 결과 ===
원본 차원: 4
축소 차원: 2

예제: Perplexity 영향 확인

# 다양한 perplexity 값으로 실험
perplexities = [5, 30, 50]
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

for idx, perp in enumerate(perplexities):
    tsne_temp = TSNE(n_components=2, random_state=42, perplexity=perp)
    X_tsne_temp = tsne_temp.fit_transform(X_scaled)
    
    scatter = axes[idx].scatter(X_tsne_temp[:, 0], X_tsne_temp[:, 1], 
                               c=y.astype('category').cat.codes,
                               cmap='viridis', alpha=0.6, edgecolors='k')
    axes[idx].set_title(f't-SNE (perplexity={perp})')
    axes[idx].set_xlabel('Component 1')
    axes[idx].set_ylabel('Component 2')
    axes[idx].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

21.3.2 UMAP

UMAP(Uniform Manifold Approximation and Projection)은 데이터가 저차원 다양체에 놓여 있다고 가정하고, 전역 구조와 국소 구조를 동시에 보존하려는 비선형 차원 축소 방법이다.

UMAP의 특징

특징 설명
t-SNE보다 빠름 대규모 데이터 처리 가능
전역+국소 구조 보존 균형 잡힌 표현
재현성 좋음 같은 설정에서 일관된 결과
다목적 시각화 + 전처리 모두 가능
하이퍼파라미터 민감 튜닝 필요

UMAP 하이퍼파라미터

파라미터 설명 권장값
n_components 출력 차원 2 또는 3
n_neighbors 이웃 수 15 (기본값)
min_dist 점 간 최소 거리 0.1 (기본값)
metric 거리 측정 방법 ‘euclidean’
random_state 재현성 고정값

예제: UMAP 적용

# UMAP 설치 필요: pip install umap-learn
try:
    import umap
    
    # UMAP 적용
    umap_model = umap.UMAP(n_components=2, random_state=42)
    X_umap = umap_model.fit_transform(X_scaled)
    
    print("=== UMAP 결과 ===")
    print(f"원본 차원: {X.shape[1]}")
    print(f"축소 차원: {X_umap.shape[1]}")
    
    # 시각화
    plt.figure(figsize=(10, 6))
    scatter = plt.scatter(X_umap[:, 0], X_umap[:, 1], c=y.astype('category').cat.codes,
                         cmap='viridis', alpha=0.6, edgecolors='k')
    plt.xlabel('UMAP Component 1')
    plt.ylabel('UMAP Component 2')
    plt.title('UMAP: 2D Projection')
    plt.legend(handles=scatter.legend_elements()[0], labels=y.unique())
    plt.grid(True, alpha=0.3)
    plt.tight_layout()
    plt.show()
    
except ImportError:
    print("UMAP이 설치되지 않았습니다. 설치하려면: pip install umap-learn")
UMAP이 설치되지 않았습니다. 설치하려면: pip install umap-learn

21.4 방법 간 종합 비교

PCA vs t-SNE vs UMAP 비교

구분 PCA t-SNE UMAP
유형 선형 비선형 비선형
속도 매우 빠름 느림 빠름
전역 구조 보존 우수 보통 우수
국소 구조 보존 보통 우수 우수
해석 가능성 높음 낮음 낮음
역변환 가능 불가능 불가능
재현성 완벽 낮음 높음
대용량 데이터 적합 부적합 적합
주 용도 전처리, 압축, 시각화 시각화 시각화, 전처리
모델 입력 가능 불가능 가능

예제: 세 방법 비교 시각화

# 세 가지 방법 비교
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

# PCA
scatter1 = axes[0].scatter(X_pca[:, 0], X_pca[:, 1], c=y.astype('category').cat.codes,
                           cmap='viridis', alpha=0.6, edgecolors='k')
axes[0].set_title('PCA (Linear)')
axes[0].set_xlabel(f'PC1 ({pca.explained_variance_ratio_[0]*100:.1f}%)')
axes[0].set_ylabel(f'PC2 ({pca.explained_variance_ratio_[1]*100:.1f}%)')
axes[0].grid(True, alpha=0.3)

# t-SNE
scatter2 = axes[1].scatter(X_tsne[:, 0], X_tsne[:, 1], c=y.astype('category').cat.codes,
                           cmap='viridis', alpha=0.6, edgecolors='k')
axes[1].set_title('t-SNE (Non-linear)')
axes[1].set_xlabel('Component 1')
axes[1].set_ylabel('Component 2')
axes[1].grid(True, alpha=0.3)

# UMAP (설치된 경우)
try:
    scatter3 = axes[2].scatter(X_umap[:, 0], X_umap[:, 1], c=y.astype('category').cat.codes,
                               cmap='viridis', alpha=0.6, edgecolors='k')
    axes[2].set_title('UMAP (Non-linear)')
    axes[2].set_xlabel('Component 1')
    axes[2].set_ylabel('Component 2')
    axes[2].grid(True, alpha=0.3)
except:
    axes[2].text(0.5, 0.5, 'UMAP not installed', 
                ha='center', va='center', fontsize=14)
    axes[2].set_title('UMAP (Not Available)')

plt.tight_layout()
plt.show()

21.5 실무 활용 가이드

사용 목적별 방법 선택

목적 권장 방법 이유
모델 학습 전 전처리 PCA 빠르고 안정적, 역변환 가능
데이터 탐색 및 시각화 t-SNE 또는 UMAP 비선형 구조 잘 표현
대용량 데이터 시각화 UMAP 속도와 품질의 균형
노이즈 제거 PCA 주요 신호만 추출
다중공선성 제거 PCA 독립적인 축 생성
클러스터링 전처리 PCA 또는 UMAP 차원 줄여 효율성 향상

데이터 크기별 권장

샘플 수 변수 수 권장 방법
< 1,000 < 50 PCA, t-SNE, UMAP 모두 가능
< 10,000 50~100 PCA, UMAP (t-SNE 주의)
> 10,000 > 100 PCA, UMAP (t-SNE 피함)

파이프라인 예제

from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

# PCA + 분류 모델 파이프라인
pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('pca', PCA(n_components=0.95)),
    ('classifier', RandomForestClassifier(random_state=42))
])

# 교차 검증
scores = cross_val_score(pipeline, X, y, cv=5)

print("=== PCA + RandomForest 파이프라인 ===")
print(f"교차 검증 정확도: {scores.mean():.4f} ± {scores.std():.4f}")
=== PCA + RandomForest 파이프라인 ===
교차 검증 정확도: 0.9791 ± 0.0152

21.6 주의사항

차원 축소 사용 시 주의점

주의사항 설명 대응 방법
정보 손실 차원 축소 시 일부 정보 손실 불가피 설명 분산 비율 확인
해석 어려움 새로운 변수의 의미 파악 곤란 PCA 로딩 분석
타겟 미반영 비지도 학습이라 타겟 고려 안 함 LDA 등 지도 학습 차원 축소 고려
스케일 민감 변수 스케일에 따라 결과 달라짐 반드시 표준화
과적합 위험 t-SNE 등은 학습 데이터에만 적용 테스트셋 변환 시 주의
하이퍼파라미터 설정에 따라 결과 크게 달라짐 여러 값 실험

체크리스트

21.7 요약

이 장에서는 고차원 데이터를 저차원으로 변환하는 차원 축소 기법을 학습했다. 주요 내용은 다음과 같다.

핵심 요약

  • 목적: 차원의 저주 해결, 시각화, 학습 효율화
  • PCA: 선형, 빠름, 해석 가능, 전처리 및 시각화
  • t-SNE: 비선형, 시각화 특화, 국소 구조 보존
  • UMAP: 비선형, 빠름, 전역+국소 보존, 다목적

선택 가이드

  • 전처리 목적 → PCA
  • 시각화 목적 (소규모) → t-SNE
  • 시각화 목적 (대규모) → UMAP
  • 균형 잡힌 접근 → PCA로 시작, 필요시 UMAP

차원 축소는 데이터의 본질적인 구조를 파악하고 효율적으로 분석하는 강력한 도구이다. 데이터 특성과 분석 목적에 맞는 방법을 선택하고, 결과를 시각화하여 검증하는 것이 중요하다.